#include "GSharpClass.h"

#include "ScriptRuntime/ScriptSubsystem.h"
#include "ScriptRuntime/Type/ScriptContent.h"
#include "ScriptRuntime/Type/FieldValuesContainer.h"
#include "ScriptRuntime/Type/ScriptContent.h"

#include "Object/Object.h"

#include "Reflection/NativeClass.h"
#include "Reflection/FunctionList.h"
#include "Reflection/Function.h"

#include "ScriptRuntime/Type/GSharpFieldProperty.h"
#include "ScriptRuntime/Type/GSharpPropertyList.h"



void CGSharpClass::OnPostConstruct(CGSharpClass * InClass)
{

    assert(InClass->Properties->IsScriptPropertyList());
    CGSharpPropertyList * GSharpFields = static_cast<CGSharpPropertyList*>(InClass->Properties);
    InClass->StaticFieldValues=GSharpFields->CreateStaticFieldValues();

    //invoke static constructor if exist
    for(auto & Function: InClass->Functions->Functions)
    {
        if(Function->IsStatic() && Function->IsConstructor())
        {
            TVector<CAny> Args;
            Function->Invoke(Args);
            break;
        }
    }
}

CNativeClass *CGSharpClass::GetParentNativeClass()
{
    assert(BaseType&& "require a base type");

    CType * NativeBase=BaseType;
    while(NativeBase->IsScriptClassType())
    {
        NativeBase=NativeBase->BaseType;
    }

    return static_cast<CNativeClass*>(NativeBase);
}

CGSharpFieldValuesContainer *CGSharpClass::GetStaticValues() const
{
    return StaticFieldValues;
}

CGSharpClass::CGSharpClass(
    const STypeID &InTypeID
    , ETypeFlags InExtraFlags
    )
:CClass(
    InTypeID
    ,ETypeFlags::Script | InExtraFlags
    )
{
    Instancable=new CScriptClassInstance(this);
    Properties=new CGSharpPropertyList(this);
    PostConstructFunction=[this](){
        OnPostConstruct(this);
    };

    //create shared ptr type
    {
        this->SharedPtrType=new CObjectPtrTypeGeneralized(this);
    }
}

CGSharpClass::~CGSharpClass()
{
    if(StaticFieldValues)
    {
        delete StaticFieldValues;
    }

}

CString CGSharpClass::CScriptClassInstance::ToDebugString(const void *Instance) const
{
    return ClassType->GetFullName()+ U"//"+CString::CreateHex((int64_t)Instance,true);
}


int64_t CGSharpClass::CScriptClassInstance::GetDebugNamedChildNum(const void *Instance) const
{ 
    return ClassType->Properties->GetNonStaticFieldProperties().Num();
}

CType *CGSharpClass::CScriptClassInstance::GetDebugNamedChildType(int64_t Index, const void *Instance) const
{ 
    return ClassType->Properties->GetNonStaticFieldProperties()[Index]->GetType().BasicType;
}

CString CGSharpClass::CScriptClassInstance::GetDebugNamedChildName(int64_t Index, const void *Instance) const
{ 
    return ClassType->Properties->GetNonStaticFieldProperties()[Index]->GetName();
}

const void *CGSharpClass::CScriptClassInstance::GetDebugNamedChildValue(int64_t Index, const void *Instance) const
{
    return ClassType->Properties->GetNonStaticFieldProperties()[Index]->FieldProperty->GetValuePointer(Instance);
}

uint32_t CGSharpClass::CScriptClassInstance::GetSize()
{
    CClass* NativeClass = ClassType->GetParentNativeClass();
    return NativeClass->GetSize();
}

uint64_t CGSharpClass::CScriptClassInstance::GetInstanceHash(const void *Instance) const
{
    return (uint64_t)Instance;
}

bool CGSharpClass::CScriptClassInstance::IsInstanceEqual(const void *Instance0, const void *Instance1) const
{
    return Instance0==Instance1;
}

void CGSharpClass::CScriptClassInstance::InitInstance(void *Memory)
{
    CNativeClass* NativeClass = ClassType->GetParentNativeClass();
    RObject* NativeObject=NativeClass->NewScriptNativeInstance(Memory);
    NativeObject->SetScriptContent(new CScriptContent(ClassType));
}

void CGSharpClass::CScriptClassInstance::EraseInstance(void *Instance)
{
    //2. delete script content
    RObject* ScriptObject = (RObject*)Instance;
    CScriptContent* ScriptContent = ScriptObject->GetScriptContent();
    delete ScriptContent;
    ScriptObject->SetScriptContent(nullptr);

    //3. delete native object
    CNativeClass* NativeClass = ClassType->GetParentNativeClass();
    NativeClass->EraseInstance(Instance);
}

void CGSharpClass::CScriptClassInstance::CloneInstance(const void *Instance, void *TargetMemory)
{
    assert(false && "unimplemented");
}

bool CGSharpClass::CScriptClassInstance::IsForceConvertableTo(CType *Other)
{
    if(Other==ClassType)
    {
        return true;
    }

    if(Other->IsClassType())
    {
        CClass* OtherClass = (CClass*)Other;
        if(ClassType->IsDescendantOf(OtherClass))
        {
            return true;
        }
    }

    return false;
}

void *CGSharpClass::CScriptClassInstance::CreateInstance()
{
    assert(false && "class donot  support allocate now ");
    return nullptr;
}

void CGSharpClass::CScriptClassInstance::ResetAllocated(void *InMemory)
{
    assert(false && "class donot  support allocate now ");
}
